#ifndef __LOGGER_H_
#define __LOGGER_H_

#include <string>
#include <thread>
#include <map>
#include <fstream>

#include "BlockingQueue.h"

namespace Util
{
	enum LogLevel
	{
		Level_InfoLog = 0,
		Level_WarnLog,
		Level_ErrorLog
	};
	struct stLogEntry
	{
		LogLevel _level;
		std::string _time;
		std::string _message;
		std::string _function;
		std::string _file;
		DWORD _line;
		DWORD _thread_id;
	};
	class Logger
	{
	public:
		Logger(LogLevel level, const char* func, const char* file, DWORD line)
			:_level(level), _function(func), _file(file), _line(line)
		{}
		~Logger()
		{
			if (!_message.empty())
			{
				add_log(_level, _message, _function, _file, _line);
				_message.clear();
			}
		}

		friend inline Logger& operator << (Logger& source, char* append)
		{
			source._message.append(append);
			return source;
		}

		friend inline Logger& operator << (Logger& source, const char* append)
		{
			source._message.append(append);
			return source;
		}

		friend inline Logger& operator << (Logger& source, int append)
		{
			source._message.append(std::to_string(append));
			return source;
		}

		friend inline Logger& operator << (Logger& source, unsigned int append)
		{
			source._message.append(std::to_string(append));
			return source;
		}

		friend inline Logger& operator << (Logger& source, long append)
		{
			source._message.append(std::to_string(append));
			return source;
		}

		friend inline Logger& operator << (Logger& source, unsigned long append)
		{
			source._message.append(std::to_string(append));
			return source;
		}

		friend inline Logger& operator << (Logger& source, long long append)
		{
			source._message.append(std::to_string(append));
			return source;
		}

		friend inline Logger& operator << (Logger& source, unsigned long long append)
		{
			source._message.append(std::to_string(append));
			return source;
		}

		friend inline Logger& operator << (Logger& source, float append)
		{
			source._message.append(std::to_string(append));
			return source;
		}

		friend inline Logger& operator << (Logger& source, double append)
		{
			source._message.append(std::to_string(append));
			return source;
		}

		friend inline Logger& operator << (Logger& source, long double append)
		{
			source._message.append(std::to_string(append));
			return source;
		}
				
	public:
		static void start_logging(const char* dir, bool infolog, bool warnlog, bool errorlog);
		static void stop_logging();

	private:
		static std::thread* _write_log_thread;
		static BlockingQueue<stLogEntry> _log_list;
		static std::string _log_dir;
		static std::map<LogLevel, bool> _log_flg;
		static bool _run_flg;

		static void add_log(LogLevel level, std::string message, std::string function, std::string file, DWORD line);
		static void open_log_file(std::map<LogLevel, std::ofstream> &log_file, std::string file_date);
		static void close_log_file(std::map<LogLevel, std::ofstream> &log_file);
		static void write_log();

	private:
		LogLevel _level;
		std::string _message;
		std::string _function;
		std::string _file;
		DWORD _line;
	};
}

#endif //__LOGGER_H_